﻿//using System;
//using System.Collections.Generic;
//using System.Data;
//using System.Data.Entity;
//using System.Diagnostics;
//using System.Linq;
//using System.Threading;
//using UCosmic.Domain;
//using UCosmic.Domain.Identity;
//using UCosmic.Orm;

//namespace UCosmic.Storage
//{
//    public class RoleStorage : IStoreRoles
//    {
//        #region Construction & DI

//        private UCosmicContext DbContext { get; set; }

//        public RoleStorage(IUnitOfWork unitOfWork)
//        {
//            DbContext = unitOfWork as UCosmicContext;
//        }

//        public IUnitOfWork UnitOfWork { get { return DbContext; } }

//        #endregion
//        #region Get / Read

//        public IEnumerable<Role> Find(RoleFinder finder)
//        {
//            // do not track entities returned by find methods
//            var query = DbContext.Roles.AsQueryable();

//            // initialize finder
//            if (finder == null)
//                finder = new RoleFinder();

//            // apply context tracking
//            if (!finder.IsForInsertOrUpdate)
//                query = query.AsNoTracking();

//            // apply includes
//            var includes = finder.ToBeEagerLoaded;
//            if (includes != null && includes.Count > 0)
//                query = finder.ToBeEagerLoaded.Aggregate(query, (lastInclude, nextInclude) =>
//                    lastInclude.Include(nextInclude));

//            // apply current, archived, and deleted switches
//            if (finder.IsCurrent.HasValue)
//                query = query.Where(role => role.IsCurrent == finder.IsCurrent.Value);
//            if (finder.IsArchived.HasValue)
//                query = query.Where(role => role.IsArchived == finder.IsArchived.Value);
//            if (finder.IsDeleted.HasValue)
//                query = query.Where(role => role.IsDeleted == finder.IsDeleted.Value);

//            // apply entity id
//            if (finder.EntityId.HasValue && finder.EntityId != Guid.Empty)
//                query = query.Where(role => role.EntityId == finder.EntityId);

//            // apply revision id
//            if (finder.RevisionId.HasValue && finder.RevisionId != 0)
//                query = query.Where(role => role.RevisionId == finder.RevisionId);

//            // apply granted to username 
//            if (!string.IsNullOrWhiteSpace(finder.GrantedToUserName))
//                query = query.Where(role => role.Grants.Any(grant => 
//                    grant.IsCurrent && !grant.IsArchived && !grant.IsDeleted 
//                        && grant.User.UserName.Equals(finder.GrantedToUserName, StringComparison.OrdinalIgnoreCase)));

//            var found = query.ToList();
//            return found;
//        }

//        public Role Get(RoleFinder finder)
//        {
//            return Find(finder).SingleOrDefault();
//        }

//        #endregion
//        #region Set / Write

//        public Role Revise(Role revisedRole)
//        {
//            if (revisedRole == null) // cannot revise a null entity
//                throw new ArgumentNullException("revisedRole");
//            var userVersion = revisedRole;

//            var context = DbContext; // shorthand code
//            var userEntry = context.Entry(userVersion); // ensure that the revised entity argument
//            userEntry.State = EntityState.Detached; // is detached from the db context

//            Role currentVersion = null; // role that is attached to the db context
//            if (userVersion.RevisionId != 0) // search for role in the db
//                currentVersion = context.Roles
//                    .Include(r => r.Grants.Select(g => g.User))
//                    .SingleOrDefault(r => r.RevisionId == userVersion.RevisionId
//                        && r.EntityId == userVersion.EntityId // match both revision id and entity id
//                        && r.EntityId != Guid.Empty); // ignore empty entity ids

//            if (currentVersion == null && userVersion.RevisionId == 0)
//            { // when the db has no matching role, and the revision id is zero, 
//                userEntry.State = EntityState.Added; // add revision to the db
//                context.SaveChanges(); // commit the add to the db
//                userEntry.State = EntityState.Detached; // ensure that a detached
//                return userVersion; // entity is returned to the subscriber
//            }

//            // when the role already exists, revise it.
//            var currentEntry = context.Entry(currentVersion); // get the entry for the db role
//            var previousVersion = (Role)CreatePreviousVersion(currentVersion);
//            var previousEntry = context.Entry(previousVersion); // get the entry for the db role
//            #region Diagnostic Assertions
//            Debug.Assert(currentVersion != null, "The value of 'dbRole' should never be null.");
//            Debug.Assert(previousVersion != null, "The value of 'oldRole' should never be null.");
//            Debug.Assert(previousEntry.State == EntityState.Detached,
//                "The 'oldRole' should be always be detached from the context");
//            #endregion

//            // revise grants
//            if (userVersion.Grants != null)
//                foreach (var revisedGrant in userVersion.Grants)
//                {
//                    revisedGrant.Role = userVersion;
//                    Revise(revisedGrant);
//                }

//            // do not update the entity unless
//            if (!previousVersion.Name.Equals(userVersion.Name) // role name has changed
//                || !previousVersion.Description.Equals(userVersion.Description)) // or description has changed
//            {
//                currentEntry.CurrentValues.SetValues(userVersion); // copy scalar properties from revision to db
//            }

//            // do not store a new revision unless current has been modified
//            if (currentEntry.State == EntityState.Modified)
//            {
//                UpdateCurrentVersion(currentVersion);
//                previousEntry.State = EntityState.Added; // and add it to the db for auditing
//            }

//            context.SaveChanges();
//            currentEntry.State = EntityState.Detached;
//            return currentVersion;
//        }

//        private void Revise(RoleGrant revisedGrant, Role previousRole = null)
//        {
//            var context = DbContext;
//            var userVersion = revisedGrant;
//            var aggregateVersion = context.Roles.Local // get the principal entity from local collection
//                .SingleOrDefault(role => role.RevisionId == userVersion.Role.RevisionId
//                    && role.EntityId != Guid.Empty); // match role primary key to grant foreign key
//            if (aggregateVersion == null) return; // revise only if principal has been loaded
//            var aggregateEntry = context.Entry(aggregateVersion); // will operate on principal entry

//            // 1.) Add new grants
//            if (userVersion.RevisionId == 0 && !userVersion.IsDeleted)
//            {
//                // make sure the new entity id does not already exist in the db
//                var currentVersion = aggregateVersion.Grants.SingleOrDefault(g => g.EntityId == userVersion.EntityId);
//                if (currentVersion == null) // do not add if already exists
//                {
//                    userVersion.User = context.Users.Find(userVersion.User.RevisionId);
//                    context.Entry(userVersion).State = EntityState.Added; // add the grant
//                    aggregateEntry.State = EntityState.Modified; // modify the principal
//                }
//            }

//            // 2.) Remove existing grants
//            else if (userVersion.RevisionId != 0 && userVersion.IsDeleted)
//            {
//                // make sure the grant exists and can be deleted
//                var currentVersion = aggregateVersion.Grants.SingleOrDefault(g => g.RevisionId == userVersion.RevisionId
//                    && g.EntityId == userVersion.EntityId && g.EntityId != Guid.Empty);
//                if (currentVersion != null) // do not remove unless grant already exists
//                {
//                    if (previousRole != null)
//                    {
//                        currentVersion.IsDeleted = true; // grant is deleted
//                        currentVersion.IsCurrent = false; // grant is no longer current
//                        currentVersion.Role = previousRole; // attach the grant to the previous revision
//                    }
//                    else
//                    {
//                        context.Entry(currentVersion).State = EntityState.Deleted;
//                    }
//                    aggregateEntry.State = EntityState.Modified; // modify the principal
//                }
//            }

//            // 3.) Update existing grants
//            else if (userVersion.RevisionId != 0 && !userVersion.IsDeleted)
//            {
//                // make sure the grant exists and can be updated
//                var currentVersion = aggregateVersion.Grants.SingleOrDefault(g => g.RevisionId == userVersion.RevisionId
//                    && g.EntityId == userVersion.EntityId && g.EntityId != Guid.Empty);
//                if (currentVersion != null)
//                {   // do not update grants that do not exist or have not changed
//                    if (currentVersion.ForEstablishment != userVersion.ForEstablishment)
//                    {
//                        var currentEntry = context.Entry(currentVersion); // get an entry for the previous version
//                        if (previousRole != null)
//                        {
//                            var previousVersion = (RoleGrant)CreatePreviousVersion(currentVersion);
//                            previousRole.Grants.Add(previousVersion); // previous grant belongs to previous principal
//                        }
//                        currentEntry.CurrentValues.SetValues(userVersion); // copy scalar properties from revision to db
//                        UpdateCurrentVersion(currentVersion);
//                        aggregateEntry.State = EntityState.Modified; // modify the principal
//                    }
//                }
//            }
//        }

//        private RevisableEntity CreatePreviousVersion(RevisableEntity previousState)
//        {
//            var previous = (RevisableEntity)DbContext.Entry(previousState)
//                .OriginalValues.ToObject(); // create previous version as copy
//            previous.RevisionId = 0; // previous will be added, need primary key changed
//            previous.IsCurrent = false; // previous versions are flagged as not being current
//            return previous;
//        }

//        private static void UpdateCurrentVersion(RevisableEntity currentState)
//        {
//            currentState.CreatedOnUtc = DateTime.UtcNow; // update the current revision values
//            currentState.CreatedByPrincipal = Thread.CurrentPrincipal.Identity.Name;
//        }

//        //public Role ReviseV1(Role revisableEntity)
//        //{
//        //    if (revisableEntity == null) return null;

//        //    // check whether entity already exists in the db
//        //    var existingEntity = DbContext.Roles.ById(revisableEntity.RevisionId);

//        //    if (existingEntity == null)
//        //    {
//        //        DbContext.Entry(revisableEntity).State = (revisableEntity.RevisionId == 0)
//        //            ? EntityState.Added
//        //            : EntityState.Modified;
//        //        existingEntity = revisableEntity;
//        //    }
//        //    else
//        //    {
//        //        var existingEntry = DbContext.Entry(existingEntity);
//        //        existingEntry.CurrentValues.SetValues(revisableEntity);
//        //        existingEntity.CreatedOnUtc = DateTime.UtcNow;
//        //        existingEntity.CreatedByPrincipal = Thread.CurrentPrincipal.Identity.Name;
//        //        var originalEntity = existingEntry.OriginalValues.ToObject() as Role;
//        //        if (originalEntity != null)
//        //        {
//        //            if (!originalEntity.Name.Equals(existingEntity.Name)
//        //                || !originalEntity.Description.Equals(existingEntity.Description))
//        //            {
//        //                originalEntity.IsCurrent = false;
//        //                DbContext.Roles.Add(originalEntity);
//        //            }
//        //        }

//        //        // grants
//        //        if (revisableEntity.Grants != null)
//        //        {
//        //            var newGrants = revisableEntity.Grants.ToList();
//        //            var oldGrants = existingEntity.Grants.ToList();
//        //            foreach (var oldGrant in
//        //                from oldGrant in oldGrants
//        //                let matchedGrant = newGrants.SingleOrDefault(
//        //                    p => p.UserId == oldGrant.UserId && p.ForEstablishmentId == oldGrant.ForEstablishmentId)
//        //                where matchedGrant == null
//        //                select oldGrant)
//        //            {
//        //                DbContext.Entry(oldGrant).State = EntityState.Deleted;
//        //            }
//        //            foreach (var newGrant in
//        //                from newGrant in newGrants
//        //                let matchedGrant = oldGrants.SingleOrDefault(
//        //                    p => p.UserId == newGrant.UserId && p.ForEstablishmentId == newGrant.ForEstablishmentId)
//        //                where matchedGrant == null
//        //                select newGrant)
//        //            {
//        //                existingEntity.Grants.Add(newGrant);
//        //            }
//        //            DbContext.Entry(revisableEntity).State = (revisableEntity.RevisionId == 0)
//        //                ? EntityState.Added
//        //                : EntityState.Modified;
//        //        }
//        //    }

//        //    return existingEntity;
//        //}

//        //private void Refresh(Role entity)
//        //{
//        //    DbContext.Entry(entity).State = (entity.RevisionId == default(int))
//        //        ? EntityState.Added
//        //        : EntityState.Modified;
//        //}

//        //public void Delete(Role role)
//        //{
//        //    var rolesToDelete = DbContext.Roles.Where(r => r.EntityId == role.EntityId);
//        //    foreach (var roleToDelete in rolesToDelete)
//        //    {
//        //        roleToDelete.IsCurrent = false;
//        //        roleToDelete.IsDeleted = true;
//        //    }
//        //}

//        //public void Revoke(RoleGrant grant)
//        //{
//        //    DbContext.Entry(grant).State = EntityState.Deleted;
//        //}

//        #endregion

//        public void Dispose()
//        {
//            UnitOfWork.Dispose();
//        }
//    }
//}